package com.uinnova.product.eam.service.bm.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.binary.core.exception.BinaryException;
import com.binary.core.util.BinaryUtils;
import com.binary.framework.exception.ServiceException;
import com.google.common.collect.Lists;
import com.uinnova.product.eam.base.exception.ServerException;
import com.uinnova.product.eam.comm.model.es.EamCategory;
import com.uinnova.product.eam.model.bm.FlowModelMergeParams;
import com.uinnova.product.eam.service.EamCategorySvc;
import com.uinnova.product.eam.service.ICISwitchSvc;
import com.uinnova.product.eam.service.impl.IamsCIRltSwitchSvc;
import com.uinnova.product.vmdb.comm.model.ci.CcCi;
import com.uinnova.project.api.diagram.v2.client.ESDiagramApiClient;
import com.uinnova.project.base.diagram.comm.model.ESDiagram;
import com.uinnova.project.base.diagram.comm.model.ESDiagramLink;
import com.uinnova.project.base.diagram.comm.model.ESDiagramNode;
import com.uinnova.project.model.diagram.DiagramPullRequest;
import com.uinnova.project.model.diagram.DiagramPullResponse;
import com.uinnova.project.model.diagram.DiagramPushRequest;
import com.uinnova.project.model.diagram.DiagramPushResponse;
import com.uino.bean.cmdb.base.ESCIInfo;
import com.uino.bean.cmdb.base.ESCIRltInfo;
import com.uino.bean.cmdb.base.LibType;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 流程建模视图数据发布/检出业务实现
 * @author ch
 */
@Service
@Slf4j
public class FlowModelDiagramMerge {
    @Resource
    private ESDiagramApiClient diagramApiClient;
    @Autowired
    private EamCategorySvc categorySvc;
    @Resource
    private ICISwitchSvc iciSwitchSvc;
    @Autowired
    IamsCIRltSwitchSvc ciRltSwitchSvc;

    private static final String DIR_ERROR_MSG = "目录数据有误,请联系管理员!";
    public void pushCheck(FlowModelMergeParams params) {
        List<ESDiagram> diagramList = params.getMergeDiagramList();
        if(BinaryUtils.isEmpty(diagramList)){
            return;
        }
        //单图发布时校验当前视图所在目录是否发布
        if(params.getSingleFlag()){
            for (ESDiagram each : diagramList) {
                // 目录ID应该不会出现为0的情况
                if(each.getDirId().equals(0L)){
                    continue;
                }
                EamCategory privateCategory = params.getPrivateIdMap().get(each.getDirId());
                if(BinaryUtils.isEmpty(privateCategory)){
                    throw new ServerException(DIR_ERROR_MSG);
                }
                EamCategory designCategory = params.getDesignCodeMap().get(privateCategory.getCiCode());
                if(BinaryUtils.isEmpty(designCategory)){
                    throw new ServerException("资产中尚无该模型目录,请通过模型目录进行发布!");
                }
            }
        }
        //判断版本
    }

    public void push(FlowModelMergeParams params) {
        List<ESDiagram> pushDiagramList = params.getMergeDiagramList();
        if(BinaryUtils.isEmpty(pushDiagramList)){
            return;
        }
        //私有库与设计库目录映射关系,设计库视图创建时,需要根据私有库视图查找对应目录Id
        Map<String,Long> diagramIdDirIdMap = new HashMap<>(16);
        //流程建模视图特有：未发布,但可以匹配到设计库视图的映射(根据目录绑定视图id)
        Map<String,String> releaseDiagramIdMap = new HashMap<>(16);

        Map<Long, EamCategory> privateIdMap = new HashMap<>(16);
        Map<String, EamCategory> privateCodeMap = new HashMap<>(16);        // key值为目录type_ciCode 拼接
        for (EamCategory each : params.getPrivateModelList()) {
            privateIdMap.put(each.getId(), each);
            privateCodeMap.put(each.getType() + "-" + each.getCiCode(), each);
        }
        Map<String, EamCategory> designCodeMap = new HashMap<>(params.getDesignModelList().size());
        List<EamCategory> emptyIdList = new ArrayList<>();
        for (EamCategory each : params.getDesignModelList()) {
            designCodeMap.put(each.getType() + "-" + each.getCiCode(), each);
            if(BinaryUtils.isEmpty(each.getDiagramId())){
                emptyIdList.add(each);
            }
        }
        List<String> pushDiagramIds = new ArrayList<>();
        for (ESDiagram each : pushDiagramList) {
            Long dirId = each.getDirId();
            String diagramId = each.getDEnergy();
            pushDiagramIds.add(diagramId);
            EamCategory privateCategory = privateIdMap.get(dirId);
            if(dirId.equals(0L) || BinaryUtils.isEmpty(privateCategory)){
                log.info("################发布的模型视图未查询到在我的空间所属位置，发布的时候放到选择的发布位置一级");
                diagramIdDirIdMap.put(diagramId, params.getParentCategory().getId());
                continue;
            }
            EamCategory designCategory = designCodeMap.get(privateCategory.getType() + "-" + privateCategory.getCiCode());
            if(BinaryUtils.isEmpty(designCategory)){
                log.info("################发布的模型视图未查询到在仓库所属位置，发布的时候放到选择的发布位置一级");
                diagramIdDirIdMap.put(diagramId, params.getParentCategory().getId());
            }else{
                if(!BinaryUtils.isEmpty(designCategory.getDiagramId()) && diagramId.equals(privateCategory.getDiagramId())){
                    // 已经发布过的
                    releaseDiagramIdMap.put(diagramId, designCategory.getDiagramId());
                }
                diagramIdDirIdMap.put(diagramId, designCategory.getId());
            }
        }

        DiagramPushRequest releaseDiagram = new DiagramPushRequest();
        releaseDiagram.setReleaseDesc(params.getDesc());
        releaseDiagram.setDiagramIds(pushDiagramIds);
        releaseDiagram.setOwnerCode(params.getOwnerCode());
        releaseDiagram.setDiagramIdDirIdMap(diagramIdDirIdMap);
        releaseDiagram.setWriteReleaseDiagramIdMap(releaseDiagramIdMap);
        DiagramPushResponse response = diagramApiClient.diagramPush(releaseDiagram);
        if(response.getResultCode() == 0){
            throw new BinaryException(response.getErrMsg());
        }
        params.setDiagramIds(Lists.newArrayList(response.getDesignDiagramMap().values()));
        refreshDiagramData(pushDiagramIds, params.getOwnerCode());
        //更新挂载视图为空目录
        if(BinaryUtils.isEmpty(emptyIdList)){
            return;
        }
        List<ESDiagram> diagramList = diagramApiClient.selectByIds(pushDiagramIds, null, Lists.newArrayList(0));
        Map<String, String> diagramIdMap = new HashMap<>();
        for (ESDiagram each : diagramList) {
            diagramIdMap.put(each.getDEnergy(), each.getReleaseDiagramId());
        }
        List<EamCategory> updateList = new ArrayList<>();
        for (EamCategory each : emptyIdList) {
            Integer type = each.getType();
            String ciCode = each.getCiCode();
            EamCategory privateCategory = privateCodeMap.get(type + "-" + ciCode);
            if(BinaryUtils.isEmpty(privateCategory) || BinaryUtils.isEmpty(privateCategory.getDiagramId())){
                continue;
            }
            String designDiagramId = diagramIdMap.get(privateCategory.getDiagramId());
            if(BinaryUtils.isEmpty(designDiagramId)){
                continue;
            }
            each.setDiagramId(designDiagramId);
            updateList.add(each);
        }
        if(!BinaryUtils.isEmpty(updateList)){
            categorySvc.saveOrUpdateList(updateList, LibType.DESIGN);
        }
    }

    public void pullCheck(FlowModelMergeParams params) {
        //单图检出时,需校验目录是否已检出
        List<ESDiagram> diagramList = params.getMergeDiagramList();
        if(BinaryUtils.isEmpty(diagramList)){
            return;
        }
        //单图发布时校验当前视图所在目录是否发布
        if(params.getSingleFlag()){
            for (ESDiagram each : diagramList) {
                if(each.getDirId().equals(0L)){
                    continue;
                }
                EamCategory designCategory = params.getDesignIdMap().get(each.getDirId());
                if(BinaryUtils.isEmpty(designCategory)){
                    throw new ServerException(DIR_ERROR_MSG);
                }
                EamCategory privateCategory = params.getPrivateCodeMap().get(designCategory.getCiCode());
                if(BinaryUtils.isEmpty(privateCategory)){
                    throw new ServerException("本地尚无该模型目录,请通过模型目录进行检出!");
                }
            }
        }
        log.info("########### pullCheck diagram is over ##########");
    }

    public void pull(FlowModelMergeParams params) {
        List<ESDiagram> diagramList = params.getMergeDiagramList();
        //如果是从目录发布,做一遍过滤
        if(!params.getSingleFlag()){
            List<Long> dirIds = params.getDirIds();
            diagramList = diagramList.stream().filter(each -> dirIds.contains(each.getDirId())).collect(Collectors.toList());
        }
        if(BinaryUtils.isEmpty(diagramList)){
            return;
        }
        //map映射:<资产视图id,对应的私有库目录id>
        Map<String, Long> diagramDirIdMap = new HashMap<>(diagramList.size());
        Map<Long, EamCategory> designMap = params.getDesignIdMap();
        Map<String, EamCategory> privateMap = params.getPrivateCodeMap();
        for (ESDiagram each : diagramList) {
            if(each.getDirId().equals(0L)){
                diagramDirIdMap.put(each.getDEnergy(), 0L);
                continue;
            }
            EamCategory designCategory = designMap.get(each.getDirId());
            if(BinaryUtils.isEmpty(designCategory)){
                throw new ServerException(DIR_ERROR_MSG);
            }
            EamCategory privateCategory = privateMap.get(designCategory.getType() + "-" + designCategory.getCiCode());
            if(BinaryUtils.isEmpty(privateCategory)){
                throw new ServerException(DIR_ERROR_MSG);
            }
            diagramDirIdMap.put(each.getDEnergy(), privateCategory.getId());
        }
        List<String> pullDiagramIds = Lists.newArrayList(diagramDirIdMap.keySet());
        //处理检出视图
        DiagramPullRequest request = new DiagramPullRequest();
        request.setOwnerCode(params.getOwnerCode());
        request.setDesignIdMap(diagramDirIdMap);
        request.setDiagramIds(pullDiagramIds);
        request.setDiagramIdMap(params.getSyncDiagramMap());
        DiagramPullResponse response = diagramApiClient.diagramPull(request);
        if (response.getResultCode() == 0) {
            throw new ServiceException(response.getErrMsg());
        }
        //设计库私有库视图idMap
        Map<String, String> idMap = response.getDiagramMap();
        refreshDiagramData(new ArrayList<>(idMap.values()), params.getOwnerCode());
        List<EamCategory> saveCategorys = new ArrayList<>();
        Map<String, EamCategory> designCodeMap = new HashMap<>();
        for (EamCategory designCategory : params.getDesignModelList()) {
            designCodeMap.put(designCategory.getType() + "-" + designCategory.getCiCode(), designCategory);
        }
        for (EamCategory each : params.getPrivateModelList()) {

            EamCategory designCategory = designCodeMap.get(each.getType() + "-" + each.getCiCode());
            if(BinaryUtils.isEmpty(designCategory) || BinaryUtils.isEmpty(designCategory.getDiagramId())){
                continue;
            }
            String privateDiagramId = idMap.get(designCategory.getDiagramId());
            if(!BinaryUtils.isEmpty(privateDiagramId)){
                each.setDiagramId(privateDiagramId);
                saveCategorys.add(each);
            }
        }
        categorySvc.saveOrUpdateList(saveCategorys, LibType.PRIVATE);
    }

    /**
     * 检出操作后，刷新视图数据，保持视图上id和私有库数据id一致
     * @param energyIds 视图id
     * @param ownerCode 用户code
     */
    public void refreshDiagramData(List<String> energyIds, String ownerCode) {
        if(CollectionUtils.isEmpty(energyIds)){
            return;
        }
        Long[] diagramIds = diagramApiClient.queryDBDiagramInfoBydEnergy(energyIds.toArray(new String[0]));
        if(BinaryUtils.isEmpty(diagramIds)){
            return;
        }
        refreshDiagramNodeCiId(Lists.newArrayList(diagramIds), ownerCode);
        refreshDiagramLinkRltId(Lists.newArrayList(diagramIds), ownerCode);
    }

    /**
     * 检出操作后,刷新视图节点中ciId
     * @param diagramIds 视图id
     * @param ownerCode 用户code
     */
    public void refreshDiagramNodeCiId(List<Long> diagramIds, String ownerCode) {
        List<ESDiagramNode> nodeList = diagramApiClient.selectNodeByDiagramIds(diagramIds);
        if(BinaryUtils.isEmpty(nodeList)){
            return;
        }
        List<String> ciCodes = nodeList.stream().map(ESDiagramNode::getCiCode).filter(Objects::nonNull).distinct().collect(Collectors.toList());
        if(BinaryUtils.isEmpty(ciCodes)){
            return;
        }
        List<ESCIInfo> ciList = iciSwitchSvc.getCiByCodes(ciCodes, ownerCode, LibType.PRIVATE);
        Map<String, ESCIInfo> ciMap = ciList.stream().collect(Collectors.toMap(CcCi::getCiCode, each -> each, (k1, k2) -> k2));
        for (ESDiagramNode node : nodeList) {
            if(BinaryUtils.isEmpty(node.getNodeJson()) || BinaryUtils.isEmpty(node.getCiCode()) || ciMap.get(node.getCiCode())==null){
                continue;
            }
            JSONObject jsonObject = JSON.parseObject(node.getNodeJson());
            jsonObject.put("ciId", ciMap.get(node.getCiCode()).getId());
            node.setNodeJson(jsonObject.toJSONString());
        }
        diagramApiClient.saveNodeList(nodeList);
    }

    /**
     * 检出操作后,刷新视图关系线中RltId
     * @param diagramIds 视图id
     * @param ownerCode 用户code
     */
    public void refreshDiagramLinkRltId(List<Long> diagramIds, String ownerCode) {
        List<ESDiagramLink> linkList = diagramApiClient.selectLinkByDiagramIds(diagramIds);
        if(BinaryUtils.isEmpty(linkList)){
            return;
        }
        Set<String> uniqueCodes = linkList.stream().map(ESDiagramLink::getUniqueCode).filter(Objects::nonNull).collect(Collectors.toSet());
        if(BinaryUtils.isEmpty(uniqueCodes)){
            return;
        }
        List<ESCIRltInfo> rltList = ciRltSwitchSvc.getRltByUniqueCodes(uniqueCodes, ownerCode, LibType.PRIVATE);
        if(BinaryUtils.isEmpty(rltList)){
            log.error("未查询到关系数据:{}", uniqueCodes);
            return;
        }
        Map<String, ESCIRltInfo> rltMap = rltList.stream().collect(Collectors.toMap(ESCIRltInfo::getUniqueCode, e -> e, (k1, k2) -> k2));
        for (ESDiagramLink link : linkList) {
            if(BinaryUtils.isEmpty(link.getLinkJson()) || BinaryUtils.isEmpty(link.getUniqueCode()) || rltMap.get(link.getUniqueCode())==null){
                continue;
            }
            JSONObject jsonObject = JSON.parseObject(link.getLinkJson());
            jsonObject.put("rltId", rltMap.get(link.getUniqueCode()).getId());
            link.setLinkJson(jsonObject.toJSONString());
        }
        diagramApiClient.saveLinkList(linkList);
    }
}
